package com.suker.jvm.hotspot.src.share;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.suker.jvm.hotspot.src.share.vm.classfile.SignatureDescriptor;
import com.sun.org.apache.bcel.internal.Const;
import com.sun.org.apache.bcel.internal.classfile.*;
import com.sun.org.apache.bcel.internal.util.ByteSequence;

import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.util.Stack;

public class BytecodeExecute {

    private JavaClass javaClass;

    public static void main(String[] args) throws Exception {
        BytecodeExecute execute = new BytecodeExecute();
        Method mainMethod = execute.loadMainMethod();
        execute.exeMethod(mainMethod);

    }


    public Method loadMainMethod() throws IOException {
        String mainClass = "com.suker.jvm.hotspot.demo.TestJava";
        String className = StrUtil.subAfter(mainClass, ".", true);
        URL resource = ResourceUtil.getResource("//");
        String classPath = resource.getPath() + mainClass.replace(".", "/") + ".class";
        ClassParser classFileParserTest = new ClassParser(FileUtil.getInputStream(classPath), className);
        javaClass = classFileParserTest.parse();
        for (Method method : javaClass.getMethods()) {
            if ("main".equals(method.getName())) {
                return method;
            }
        }

        throw new NoSuchMethodError();
    }


    public void exeMethod(Method method) throws Exception {
        Code code = method.getCode();
        ConstantPool constantPool = method.getConstantPool();

        // 栈针
        Stack<Object> stack = new Stack<>();
        // 变量表
        Object[] localVariableTable = new Object[method.getLocalVariableTable().getTableLength()];

        int optIndex = 1;
        ByteSequence bytes = new ByteSequence(code.getCode());
        do {
            // 输出操作符下标
            System.out.print(optIndex + "  ");
            final int opcode = bytes.readUnsignedByte();
            if (opcode == Const.NOP) {
                // 什么都不做
                System.out.println("执行指令：NOP");
            } else if (opcode == Const.ACONST_NULL) {
                System.out.println("执行指令：ACONST_NULL");
                // 将NULL压入栈中
                stack.push(null);
            } else if (opcode == Const.DUP) {
                System.out.println("执行指令：DUP");
                // 复制顶栈数值
                stack.push(stack.peek());
            } else if (opcode == Const.INVOKESTATIC) {
                // 调用静态方法
                System.out.println("执行指令：INVOKESTATIC");
                short index = bytes.readShort();
                ConstantMethodref constant = (ConstantMethodref) constantPool.getConstant(index);
                String className = constant.getClass(constantPool);
                ConstantNameAndType constantNameAndType = (ConstantNameAndType) constantPool.getConstant(constant.getNameAndTypeIndex());
                String methodName = constantNameAndType.getName(constantPool);
                String signature = constantNameAndType.getSignature(constantPool);
                SignatureDescriptor descriptor = new SignatureDescriptor(signature);

                Class<?>[] paramsClass = descriptor.getParams();
                // 获取入参
                Object[] objs = new Object[paramsClass.length];
                for (int j = 0; j < objs.length; j++) {
                    objs[j] = stack.pop();
                }
                objs = ArrayUtil.reverse(objs);
                // 执行静态方法方法
                Class<?> aClass = Class.forName(className);
                Object result = aClass.getMethod(methodName, descriptor.getParams()).invoke(aClass, objs);
                stack.push(result);
            } else if (opcode == Const.INVOKEVIRTUAL) {
                System.out.println("执行指令：INVOKEVIRTUAL");
                short index = bytes.readShort();
                ConstantMethodref constant = (ConstantMethodref) constantPool.getConstant(index);
                ConstantNameAndType constantNameAndType = (ConstantNameAndType) constantPool.getConstant(constant.getNameAndTypeIndex());
                String className = constant.getClass(constantPool);
                String methodName = constantNameAndType.getName(constantPool);
                String signature = constantNameAndType.getSignature(constantPool);
                SignatureDescriptor descriptor = new SignatureDescriptor(signature);

                Class<?>[] paramsClass = descriptor.getParams();
                // 获取入参
                Object[] objs = new Object[paramsClass.length];
                for (int j = 0; j < objs.length; j++) {
                    objs[j] = stack.pop();
                }
                objs = ArrayUtil.reverse(objs);
                // 执行方法
                Class.forName(className).getMethod(methodName, descriptor.getParams()).invoke(stack.pop(), objs);
            } else if (opcode == Const.RETURN) {
                System.out.println("执行指令：RETURN");
            } else if (opcode == Const.LDC) {
                System.out.println("执行指令：LDC");
                byte index = bytes.readByte();
                Object value = ((ConstantString) constantPool.getConstant(index)).getConstantValue(constantPool);
                stack.push(value);
            } else if (opcode == Const.GETSTATIC) { // 获取静态属性
                System.out.println("执行指令：GETSTATIC");
                // 获取常量池下标
                short index = bytes.readShort();
                Constant constant = constantPool.getConstant(index);
                ConstantFieldref constantFieldref = (ConstantFieldref) constant;
                String className = constantFieldref.getClass(constantPool);
                // 获取字段名称
                ConstantNameAndType nameAndType = (ConstantNameAndType) constantPool.getConstant(constantFieldref.getNameAndTypeIndex());
                String fieldName = nameAndType.getName(constantPool);
                java.lang.reflect.Field field = Class.forName(className).getField(fieldName);
                Object value = field.get(null);
                stack.push(value);
            } else if (opcode == Const.ICONST_0) {
                System.out.println("执行指令：ICONST_0");
                stack.push(0);
            } else if (opcode == Const.ICONST_1) {
                System.out.println("执行指令：ICONST_1");
                stack.push(1);
            } else if (opcode == Const.ICONST_2) {
                System.out.println("执行指令：ICONST_2");
                stack.push(2);
            } else if (opcode == Const.ICONST_3) {
                System.out.println("执行指令：ICONST_3");
                stack.push(3);
            } else if (opcode == Const.ICONST_4) {
                System.out.println("执行指令：ICONST_4");
                stack.push(4);
            } else if (opcode == Const.ICONST_5) {
                System.out.println("执行指令：ICONST_5");
                stack.push(5);
            } else if (opcode == Const.LCONST_0) {
                System.out.println("执行指令：LCONST_0");
                stack.push(0);
            } else if (opcode == Const.LCONST_1) {
                System.out.println("执行指令：LCONST_1");
                stack.push(1);
            } else if (opcode == Const.ANEWARRAY) {
                System.out.println("执行指令：ANEWARRAY");
                short index = bytes.readShort();
                String className = constantPool.getConstantString(index, Const.CONSTANT_Class);
                int size = (int) stack.pop();
                // new一个数组压入栈中
                Object array = Array.newInstance(Class.forName(className.replace("/", ".")), size);
                stack.push(array);
            } else if (opcode == Const.AASTORE) {
                System.out.println("执行指令：AASTORE");
                Object arrayFef = stack.pop(); // 值
                int index = (int) stack.pop();
                Object[] array = (Object[]) stack.pop();
                array[index] = arrayFef;
            } else if (opcode == Const.ISTORE_1) {
                System.out.println("执行指令：ISTORE_1");
                // 将顶栈值压入局部变量表中
                Object value = stack.pop();
                localVariableTable[1] = value;
            } else if (opcode == Const.ASTORE_0) {
                // 将顶栈值存入第一个应用变量
                System.out.println("执行指令：ASTORE_0");
                localVariableTable[0] = stack.pop();
            } else if (opcode == Const.ASTORE_1) {
                // 将顶栈值存入第二个应用变量
                System.out.println("执行指令：ASTORE_1");
                localVariableTable[1] = stack.pop();
            } else if (opcode == Const.ASTORE_2) {
                // 将顶栈值存入第三个应用变量
                System.out.println("执行指令：ASTORE_2");
                localVariableTable[2] = stack.pop();
            } else if (opcode == Const.ILOAD_1) {
                System.out.println("执行指令：ILOAD_1");
                // 将局部第二个局部变量压入顶栈
                stack.push(localVariableTable[0]);
            } else if (opcode == Const.ALOAD_0) {
                System.out.println("执行指令：ALOAD_0");
                // 将第1个引用类型本地变量推送至栈顶
                stack.push(localVariableTable[1]);
            } else if (opcode == Const.ALOAD_1) {
                System.out.println("执行指令：ALOAD_1");
                // 将第2个引用类型本地变量推送至栈顶
                stack.push(localVariableTable[1]);
            } else if (opcode == Const.ALOAD_2) {
                System.out.println("执行指令：ALOAD_2");
                // 将第3个引用类型本地变量推送至栈顶
                stack.push(localVariableTable[2]);
            }else if (opcode == Const.ALOAD_3) {
                System.out.println("执行指令：ALOAD_3");
                // 将第4个引用类型本地变量推送至栈顶
                stack.push(localVariableTable[3]);
            }  else if (opcode == Const.POP) {
                System.out.println("执行指令：POP");
                // 将栈顶数值弹出（数值不能是long或double类型的)
                if (!stack.isEmpty()) {
                    stack.pop();
                }
            } else if (opcode == Const.NEW) {
                System.out.println("执行指令：NEW");
                short index = bytes.readShort();
                String className = constantPool.constantToString(index, Const.CONSTANT_Class);
                Object obj = Class.forName(className).newInstance();
                stack.push(obj);
            } else if (opcode == Const.INVOKESPECIAL) {
                System.out.println("执行指令：INVOKESPECIAL");
                // 调用父类方法，实例初始化方法，私有方法
                short index = bytes.readShort();
                ConstantMethodref constant = (ConstantMethodref) constantPool.getConstant(index);
                String className = constant.getClass(constantPool);
                ConstantNameAndType constantNameAndType = (ConstantNameAndType) constantPool.getConstant(constant.getNameAndTypeIndex());
                String methodName = constantNameAndType.getName(constantPool);
                String signature = constantNameAndType.getSignature(constantPool);
                SignatureDescriptor descriptor = new SignatureDescriptor(signature);

                // 调用构造方法
                if ("<init>".equals(methodName)) {
                    Class<?>[] paramsClass = descriptor.getParams();
                    // 获取入参
                    Object[] objs = new Object[paramsClass.length];
                    for (int j = 0; j < objs.length; j++) {
                        objs[j] = stack.pop();
                    }
                    objs = ArrayUtil.reverse(objs);
                    // 执行方法
                    Constructor<?> constructor = Class.forName(className).getConstructor(paramsClass);
                    Object obj = constructor.newInstance(objs);
                    stack.push(obj);
                } else {
                    throw new RuntimeException("未实现的指令");
                }
            }

            optIndex++;
        } while (bytes.getIndex() < code.getCode().length);

    }


}
